import { CookieNames } from "@/shared/cookies";
import cookieService from "./cookie-service";
import { ApiErrorInfo } from "@/shared/types";
import AppError from "@/shared/app-error";
import { number } from "zod";

type OkOptions = {
  body?: any;
  headers?: [string, string][];
};

function ok({ headers, body }: OkOptions): Response {
  return Response.json(body, { headers });
}

type FailedOptions = {
  message: string;
  headers?: [string, string][];
  cause?: any;
};

function failed(
  { type, message, cause }: ApiErrorInfo,
  { status, headers }: { status: number; headers?: [string, string][] },
): Response {
  return Response.json({ message, cause, type }, { status, headers });
}

/**
 * When request is invalid.
 */
function badRequest({ headers, message, cause }: FailedOptions): Response {
  return failed({ message, cause, type: "BAD_REQUEST" }, { status: 400, headers });
}

/**
 * When request is unauthenticated. In another words, when user has not been logged in.
 */
function unauthorized({ headers, message, cause }: FailedOptions): Response {
  const _headers: [string, string][] = [["Set-Cookie", cookieService.discard(CookieNames.TOKEN)]];
  if (headers) {
    _headers.push(...headers);
  }
  return failed({ message, cause, type: "UNAUTHORIZED" }, { status: 401, headers: _headers });
}

/**
 * When request is invalid.
 */
function internalServerError({ headers, message, cause }: FailedOptions): Response {
  return failed({ message, cause, type: "INTERNAL_SERVER_ERROR" }, { status: 501, headers });
}

const ERROR_CODE_TO_STATUS: Record<ErrorCode, HttpStatus> = {
  ERR_POST_NOT_FOUND: 404,
  ERR_CANNOT_CREATE_USER: 500,
  ERR_CANNOT_CREATE_PASSWORD: 500,
  ERR_CANNOT_LOGIN: 500,
  ERR_USER_NOT_FOUND: 404,
  ERR_UNAUTHENTICATED: 401,
  ERR_BAD_PAGE_OR_SIZE: 400,
  ERR_NO_AGREEMENT: 400,
  ERR_WEEK_PASSWORD: 400,
  ERR_UNHANDLED: 500,
};

function appError(err: AppError): Response {
  const code = err.code;
  return failed(
    { message: err.message, cause: err, type: code },
    { status: code in ERROR_CODE_TO_STATUS ? ERROR_CODE_TO_STATUS[code] : 500 },
  );
}

function error({ headers, message, cause, status }: FailedOptions & { status: number }): Response {
  switch (status) {
    case 400:
      return badRequest({ headers, message, cause });
    case 401:
      return unauthorized({ headers, message, cause });
    case 500:
      return internalServerError({ headers, message, cause });
    default:
      return failed({ message, cause, type: "Unhandled status!" }, { status: 500, headers });
  }
}

const responseService = {
  ok,
  error,
  appError,
  badRequest,
  unauthorized,
  internalServerError,
};

export default responseService;
